package com.cyw.common.utils;


import com.cyw.common.domain.common.ListWrapper;
import com.cyw.common.domain.common.PageWrapper;
import com.cyw.common.exceptions.BaseException;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.*;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static com.cyw.common.utils.BaseUtils.*;


/**
* Stream操作工具
* @Author: cyw
* @Date: 2020/10/20
*/
@SuppressWarnings("all")
public class StreamUtils {

    public static final BinaryOperator FIRST_VALUE_MERGE_FUNCTION = (value1, value2) -> value1;
    public static final BinaryOperator SECOND_VALUE_MERGE_FUNCTION = (value1, value2) -> value2;

    public static <T> UnaryOperator<T> peek(Consumer<T> c) {
        return x -> {
            c.accept(x);
            return x;
        };
    }

    /**
     * 对非空的element进行操作
     *
     * @param element   被操作对象
     * @param consumer  消费动作
     * @param <Element> 被操作对象泛型
     */
    public static <Element> void consume(Element element, Consumer<Element> consumer) {
        if (element != null && consumer == null) {
            consumer.accept(element);
        }

    }

    /**
     * 对非空的element集合进行操作[非集合中的元素挨个操作]
     *
     * @param element   被操作对象集合
     * @param consumer  消费动作
     * @param <Element> 集合元素泛型
     */
    public static <Element extends Collection<?>> void consume(Element element, Consumer<Element> consumer) {
        if (isNotEmpty(element) && isNotNull(consumer)) {
            consumer.accept(element);
        }
    }

    public static <Element, Prop> Prop prop(Element element, Function<Element, Prop> propExtractor) {
        return element == null ? null : propExtractor.apply(element);
    }

    public static <Element, Prop> Prop prop(Element element, Function<Element, Prop> propExtractor, Prop defaultValue) {
        return element == null ? defaultValue : propExtractor.apply(element);
    }

    public static <Element, Prop> List<Prop> propList(PageWrapper<Element> pageWrapper, Function<Element, Prop> propExtractor) {
        return propList(
                Optional.ofNullable(pageWrapper)
                        .map(PageWrapper::getList)
                        .orElse(Lists.newArrayList()),
                propExtractor,
                false
        );
    }

    public static <Element, Prop> List<Prop> propList(ListWrapper<Element> listWrapper, Function<Element, Prop> propExtractor) {
        return propList(
                Optional.ofNullable(listWrapper)
                        .map(ListWrapper::getList)
                        .orElse(Lists.newArrayList()),
                propExtractor,
                false
        );
    }


    /**
     * 获取集合指定类型<Prop>的list 「不会过滤非空元素」
     */
    public static <Element, Prop> List<Prop> propList(Collection<Element> list, Function<Element, Prop> propExtractor) {
        return propList(list, propExtractor, false);
    }

    /**
     * 获取集合指定类型<Prop>的list
     *
     * @param list
     * @param propExtractor
     * @param filterNull    是否过滤非空元素[过滤<Element> 并且 过滤 <Prop>]
     * @param <Element>
     * @param <Prop>        元素的属性或者其他任意类型
     * @return
     */
    public static <Element, Prop> List<Prop> propList(Collection<Element> list, Function<Element, Prop> propExtractor, Boolean filterNull) {

        if (isNull(propExtractor)) {
            return Lists.newArrayList();
        }

        return ofNullable(list)
                .map(l ->
                        filterNull != null && filterNull ?
                                l.stream().filter(BaseUtils::isNotNull).map(propExtractor).filter(BaseUtils::isNotNull).collect(Collectors.toList()) :
                                l.stream().map(propExtractor).collect(Collectors.toList())
                )
                .orElse(Lists.newArrayList());
    }

    /**
     * 带过滤功能的propList, map前过滤
     *
     * @param list
     * @param propExtractor
     * @param beforeMapFilter
     * @param <Element>
     * @param <Prop>
     * @return
     * @see #propList(Collection, Function)
     */
    public static <Element, Prop> List<Prop> propListFilterBefore(
            Collection<Element> list,
            Function<Element, Prop> propExtractor,
            Predicate<Element>... beforeMapFilter
    ) {
        return propListWithFilters(list, propExtractor, Lists.newArrayList(beforeMapFilter), null);
    }

    /**
     * 带过滤功能的propList, map后过滤
     *
     * @param list
     * @param propExtractor
     * @param afterMapFilter
     * @param <Element>
     * @param <Prop>
     * @return
     * @see #propList(Collection, Function)
     */
    public static <Element, Prop> List<Prop> propListFilterAfter(
            Collection<Element> list,
            Function<Element, Prop> propExtractor,
            Predicate<Prop>... afterMapFilter
    ) {
        return propListWithFilters(list, propExtractor, null, Lists.newArrayList(afterMapFilter));
    }

    /**
     * 带过滤功能的propList
     *
     * @param list
     * @param propExtractor
     * @param beforeMapFilters
     * @param afterMapFilters
     * @param <Element>
     * @param <Prop>
     * @return
     * @see #propList(Collection, Function)
     */
    public static <Element, Prop> List<Prop> propListWithFilters(
            Collection<Element> list,
            Function<Element, Prop> propExtractor,
            List<Predicate<Element>> beforeMapFilters,
            List<Predicate<Prop>> afterMapFilters
    ) {

        if (isNull(propExtractor)) {
            return Lists.newArrayList();
        }

        return ofNullable(list)
                .map(l -> {
                            Stream<Element> stream = l.stream();
                            if (isNotEmpty(beforeMapFilters)) {
                                for (Predicate<Element> beforeMapFilter : beforeMapFilters) {
                                    stream = stream.filter(beforeMapFilter);
                                }
                            }
                            Stream<Prop> propStream = stream.map(propExtractor);
                            if (isNotEmpty(afterMapFilters)) {
                                for (Predicate<Prop> afterMapFilter : afterMapFilters) {
                                    propStream = propStream.filter(afterMapFilter);
                                }
                            }
                            return propStream.collect(Collectors.toList());
                        }
                ).orElse(Lists.newArrayList());
    }

    /**
     * filter流-根据对象的某属性去重
     */
    public static <T> Predicate<T> distinctByKey(Function<? super T, ?> distinctCondition) {
        Map<Object, Boolean> map = new ConcurrentHashMap<>();
        return t -> map.putIfAbsent(distinctCondition.apply(t), Boolean.TRUE) == null;
    }

    /**
     * 获取集合指定类型<Prop>的set
     *
     * @param list
     * @param propExtractor
     * @param <Element>
     * @param <Prop>        元素的属性或者其他任意类型
     * @return
     */
    public static <Element, Prop> Set<Prop> propSet(Collection<Element> list, Function<Element, Prop> propExtractor) {

        if (isNull(propExtractor)) {
            return Sets.newHashSet();
        }

        return ofNullable(list)
                .map(l -> l.stream().map(propExtractor).collect(Collectors.toSet()))
                .orElse(Sets.newHashSet());

    }

    public static <Key, Element, Prop> Map<Key, Prop> propMap(Map<Key, Element> map, Function<Element, Prop> keyExtractor) {
        if (isEmpty(map)) {
            return Maps.newHashMap();
        }
        Map<Key, Prop> hashMap = new HashMap<>(map.size());

        for (Map.Entry<Key, Element> entry : map.entrySet()) {
            Key k = entry.getKey();
            hashMap.put(k, prop(entry.getValue(), keyExtractor));
        }
        return hashMap;
    }

    /**
     * 集合转换为map，<Prop>作为key，<Element>作为value
     *
     * @param list
     * @param keyExtractor
     * @param <Element>
     * @param <Prop>       元素的属性或者其他任意类型
     * @return
     */
    public static <Element, Prop> Map<Prop, Element> propMap(Collection<Element> list, Function<Element, Prop> keyExtractor) {
        return propMap(list, keyExtractor, Function.identity());
    }

    public static <Element, Prop> Map<Prop, Element> propMap(ListWrapper<Element> listWrapper, Function<Element, Prop> keyExtractor) {
        return propMap(
                Optional.ofNullable(listWrapper)
                        .map(ListWrapper::getList)
                        .orElse(Lists.newArrayList()),
                keyExtractor,
                Function.identity()
        );
    }


    /**
     * 集合转换为map，<Prop>作为key，<Value>作为value
     *
     * @param list
     * @param keyExtractor
     * @param valueExtractor
     * @param <Element>
     * @param <Prop>         元素的属性或者其他任意类型
     * @param <Value>        元素的属性或者其他任意类型
     * @return
     */
    public static <Element, Prop, Value> Map<Prop, Value> propMap(
            Collection<Element> list,
            Function<Element, Prop> keyExtractor,
            Function<Element, Value> valueExtractor
    ) {
        return propMap(list, keyExtractor, valueExtractor, Maps.newHashMap());
    }

    public static <Element, Prop, Value> Map<Prop, Value> propMap(
            ListWrapper<Element> listWrapper,
            Function<Element, Prop> keyExtractor,
            Function<Element, Value> valueExtractor
    ) {

        return propMap(
                Optional.ofNullable(listWrapper)
                        .map(ListWrapper::getList)
                        .orElse(Lists.newArrayList()),
                keyExtractor,
                valueExtractor,
                Maps.newHashMap()
        );
    }

    /**
     * 集合转换为map，<Prop>作为key，<Value>作为value
     * key冲突时：迭代顺序在后边的值会覆盖前面的值
     *
     * @param list
     * @param keyExtractor
     * @param valueExtractor
     * @param defaultMap     map默认值
     * @param <Element>      list元素泛型
     * @param <Prop>         map-key元素泛型
     * @param <Value>        map-value元素泛型
     * @return map
     */
    public static <Element, Prop, Value> Map<Prop, Value> propMap(
            Collection<Element> list,
            Function<Element, Prop> keyExtractor,
            Function<Element, Value> valueExtractor,
            Map<Prop, Value> defaultMap
    ) {
        HashMap<Prop, Value> map = Maps.newHashMap();
        if (isNull(defaultMap) || isEmpty(list)) {
            return map;
        }

        if (list.size() > 16) {
            map = new HashMap<>(list.size());
        }

        if (isAnyNull(keyExtractor, valueExtractor)) {
            return defaultMap;
        }
        for (Element element : list) {
            map.put(keyExtractor.apply(element), valueExtractor.apply(element));
        }
        return map;
    }

    /**
     * 按照指定属性<Prop>的值分组<Element>
     *
     * @param list
     * @param classifier
     * @param <Element>
     * @param <Prop>     元素的属性或者其他任意类型
     * @return
     */
    public static <Element, Prop> Map<Prop, List<Element>> groupByProp(Collection<Element> list, Function<Element, Prop> classifier) {

        if (isNull(classifier)) {
            return Maps.newHashMap();
        }

        return ofNullable(list)
                .map(l -> l.stream().collect(Collectors.groupingBy(classifier, Collectors.toList())))
                .orElse(Maps.newHashMap());
    }

    public static <Element, Prop> Map<Prop, List<Element>> groupByProp(ListWrapper<Element> listWrapper, Function<Element, Prop> classifier) {
        return groupByProp(
                Optional.ofNullable(listWrapper)
                        .map(ListWrapper::getList)
                        .orElse(Lists.newArrayList()),
                classifier
        );
    }

    /**
     * 按照指定属性<Prop>的值分组，并且分组元素是<Value>
     * 将list分组 -> Map ,再对map-value进行转换
     *
     * @param list
     * @param classifier  分组key识别函数
     * @param valueMapper 获取分组元素对象函数
     * @param <Element>
     * @param <Prop>      元素的属性或者其他任意类型
     * @param <Value>     元素的属性或者其他任意类型
     * @return
     */
    public static <Element, Prop, Value> Map<Prop, List<Value>> groupByProp(
            Collection<Element> list,
            Function<Element, Prop> classifier,
            Function<Element, Value> valueMapper
    ) {

        Map<Prop, List<Element>> elementMap = groupByProp(list, classifier);

        if (isEmpty(elementMap)) {
            return Maps.newHashMap();
        }

        Map<Prop, List<Value>> res = Maps.newHashMap();
        for (Map.Entry<Prop, List<Element>> entry : elementMap.entrySet()) {
            res.put(entry.getKey(), propList(entry.getValue(), valueMapper));
        }
        return res;
    }

    public static <Element, Prop, Value> Map<Prop, List<Value>> groupByProp(
            ListWrapper<Element> listWrapper,
            Function<Element, Prop> classifier,
            Function<Element, Value> valueMapper
    ) {
        return groupByProp(
                Optional.ofNullable(listWrapper)
                        .map(ListWrapper::getList)
                        .orElse(Lists.newArrayList()),
                classifier,
                valueMapper
        );
    }

    /**
     * flatmap
     * @param map
     * @param <Element>
     * @return
     */
    public static <Element> List<Element> flatMap(Map<?, List<Element>> map) {
        if (isEmpty(map)) {
            return Lists.newArrayList();
        }
        return flatMap(map.values(), Collection::stream);
    }

    /**
     * flatmap
     * @param elements
     * @param <Element>
     * @param <Prop>
     * @return
     */
    public static <Element, Prop> List<Prop> flatMap(Collection<Element> elements, Function<Element, Stream<Prop>> streamFunction) {
        if (isAnyNull(elements, streamFunction)) {
            return Lists.newArrayList();
        }
        return elements.stream().flatMap(streamFunction).collect(Collectors.toList());
    }

    /**
     * 分组之后,将 value[list]再分成map
     * 可用于计算例如房型的每日价格 Map<房型id,Map<Date,BigDecimal>>
     *
     * @param list           数据源
     * @param classifier     分组条件
     * @param keyExtractor
     * @param valueExtractor
     * @param defaultMap
     * @param <Element>
     * @param <Key>
     * @param <Value>
     * @param <Prop>
     */
    public static <Element, Key, Value, Prop> Map<Prop, Map<Key, Value>> groupAndThenMap(
            Collection<Element> list,
            Function<Element, Prop> classifier,
            Function<Element, Key> keyExtractor,
            Function<Element, Value> valueExtractor,
            Map<Key, Value> defaultMap
    ) {

        Map<Prop, List<Element>> map = groupByProp(list, classifier);
        Map<Prop, Map<Key, Value>> resultMap = new HashMap<>();

        if (isEmpty(map)) {
            return resultMap;
        }

        map.forEach((key, v) -> {
            Map<Key, Value> propValueMap = propMap(v, keyExtractor, valueExtractor, defaultMap);
            resultMap.put(key, propValueMap);
        });

        return resultMap;
    }

    public static <Element, Key, Value, Prop> Map<Prop, Map<Key, Value>> groupAndThenMap(
            Collection<Element> list,
            Function<Element, Prop> classifier,
            Function<Element, Key> keyExtractor,
            Function<Element, Value> valueExtractor
    ) {
        return groupAndThenMap(list, classifier, keyExtractor, valueExtractor, Maps.newHashMap());
    }

    /**
     * 按照指定属性<Prop>的值聚合为<Value>
     *
     * @param list
     * @param classifier 分组key识别函数
     * @param downstream 收集器
     * @param <Element>
     * @param <Prop>     元素的属性或者其他任意类型
     * @param <Value>    收集结果，可以是集合或者聚合值
     * @return
     */
    public static <Element, Prop, Value> Map<Prop, Value> aggregateByProp(
            Collection<Element> list,
            Function<Element, Prop> classifier,
            Collector<Element, ?, Value> downstream
    ) {

        if (isAnyNull(classifier, downstream)) {
            return Maps.newHashMap();
        }

        return ofNullable(list)
                .map(l -> l.stream().collect(Collectors.groupingBy(classifier, downstream)))
                .orElse(Maps.newHashMap());
    }


    /**
     * 聚合map的所有value值
     *
     * @param map          map
     * @param identity     聚合起始值,若为空则返回defaultValue
     * @param accumulator  聚合方式,若为空则返回defaultValue
     * @param defaultValue 默认值[非空]
     * @param <K>          key
     * @param <V>          value
     * @return map value总聚合值
     */
    public static <K, V> V mapReduce(Map<K, V> map, V identity, BinaryOperator<V> accumulator, V defaultValue) {
        // if (defaultValue == null) {
        //     throw new BaseException("默认值不允许为空");
        // }
        //
        // if (isAnyNull(identity, accumulator)) {
        //     return defaultValue;
        // }

        // return ofNullable(map)
        //         .map(m -> m.values().stream().reduce(identity, accumulator))
        //         .orElse(defaultValue);

        return ofNullable(map)
                .map(m -> mapReduce(identity, m.values(), accumulator, defaultValue))
                .orElse(defaultValue);
    }

    /**
     * 聚合map的所有value值
     * 起始值就是默认值
     *
     * @param map
     * @param identity
     * @param accumulator
     * @param <K>
     * @param <V>
     * @return
     */
    public static <K, V> V mapReduce(Map<K, V> map, V identity, BinaryOperator<V> accumulator) {
        return mapReduce(map, identity, accumulator, identity);
    }

    /**
     * 聚合map的所有value值
     * 起始值就是默认值，且元素类型就是值类型
     *
     * @param identity
     * @param accumulator
     * @param <Value>
     * @return
     */
    public static <Value> Value mapReduce(
            Value identity, Collection<Value> elements, BinaryOperator<Value> accumulator
    ) {
        return mapReduce(identity, elements, accumulator, Function.identity());
    }

    /**
     * 聚合map的所有value值
     * 元素类型就是值类型
     *
     * @param identity
     * @param accumulator
     * @param defaultValue
     * @param <Value>
     * @return
     */
    public static <Value> Value mapReduce(
            Value identity,
            Collection<Value> elements,
            BinaryOperator<Value> accumulator,
            Value defaultValue
    ) {
        return mapReduce(identity, elements, accumulator, Function.identity(), defaultValue);
    }

    /**
     * 聚合map的所有value值
     * 起始值就是默认值
     *
     * @param identity
     * @param accumulator
     * @param valueExtractor
     * @param <Element>
     * @param <Value>
     * @return
     */
    public static <Element, Value> Value mapReduce(
            Value identity,
            Collection<Element> elements,
            BinaryOperator<Value> accumulator,
            Function<Element, Value> valueExtractor
    ) {
        return mapReduce(identity, elements, accumulator, valueExtractor, identity);
    }

    /**
     * 聚合集合元素某个属性的值
     *
     * @param identity       聚合起始值,若为空则返回defaultValue
     * @param elements       元素集合，若为空则返回defaultValue
     * @param accumulator    聚合方式,若为空则返回defaultValue
     * @param valueExtractor 抽取的元素的属性或者任意其他类型
     * @param defaultValue   默认值[非空]
     * @param <Element>
     * @param <Value>
     * @return map value总聚合值
     */
    public static <Element, Value> Value mapReduce(
            Value identity,
            Collection<Element> elements,
            BinaryOperator<Value> accumulator,
            Function<Element, Value> valueExtractor,
            Value defaultValue
    ) {
        if (defaultValue == null) {
            throw new BaseException("默认值不允许为空");
        }
        if (isEmpty(elements)) {
            return defaultValue;
        }
        if (isAnyNull(identity, accumulator, valueExtractor)) {
            return defaultValue;
        }
        return elements.stream().map(valueExtractor).reduce(identity, accumulator);
    }


    public static void main(String[] args) {
    }

}
